/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mukesh;

import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

/**
 * RippleButton 点击效果
 */
public class RippleButton extends Button implements Component.DrawTask, Component.TouchEventListener {
    private static final int DRAW_RADIUS_ZERO = 0;
    private static final int DRAW_RADIUS_MOVE = 50;
    private static final int DURATION = 400;
    private static final int FREQUENCY = 10;
    /**
     * 起始点
     */
    private int mDownX;
    private int mDownY;
    private float mDensity;
    /**
     * 绘制的半径
     */
    private float mDrawRadius;
    /**
     * 绘制的最大半径
     */
    private float mMaxRadius;
    private float mStepRadius;
    private boolean mDrawFinish;
    private float mCycle;
    private final Rect mRect = new Rect();
    private boolean mPressUp = false;
    private Paint mRevealPaint;
    private int mRippleColor = Color.BLACK.getValue();
    private float mAlphaFactor = 0.2f;
    private boolean mHover = true;
    /**
     * 动画
     */
    private AnimatorValue mRadiusAnimator;
    private int mDuration = 500;
    private boolean isPointMove = false;
    /**
     * 状态栏高度,跟华为官方确认过了,目前还没有提供获取状态栏高度的方法
     */
    private int mStatusBarHeight;
    private Path mClipPath = null;

    /**
     * 构造方法
     *
     * @param context Context
     */
    public RippleButton(Context context) {
        this(context, null);
    }

    /**
     * 构造方法
     *
     * @param context Context
     * @param attrSet AttrSet
     */
    public RippleButton(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    /**
     * 构造方法
     *
     * @param context   Context
     * @param attrSet   AttrSet
     * @param styleName String
     */
    public RippleButton(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        if (attrSet != null) {
            mRippleColor = AttrUtils.getColorFromAttr(attrSet, "rippleColor", mRippleColor);
            mAlphaFactor = AttrUtils.getFloatFromAttr(attrSet, "alphaFactor", mAlphaFactor);
            mHover = AttrUtils.getBooleanFromAttr(attrSet, "hover", mHover);
        }

        if (mHover) {
            initView();
            startAnimation();
            setTouchEventListener(this);
            addDrawTask(this);
            setBindStateChangedListener(new BindStateChangedListener() {
                @Override
                public void onComponentBoundToWindow(Component component) {
                    mRect.set(getLeft(), getTop(), getRight(), getBottom());
                }

                @Override
                public void onComponentUnboundFromWindow(Component component) {

                }
            });
        }
    }

    /**
     * addDrawTask
     *
     * @param task DrawTask
     */
    @Override
    public void addDrawTask(DrawTask task) {
        super.addDrawTask(task);
        task.onDraw(this, mCanvasForTaskOverContent);
    }

    /**
     * 初始化
     * initView
     */
    public void initView() {
        mCycle = DURATION / FREQUENCY;
        mDensity = DisplayManager.getInstance().getDefaultDisplay(getContext()).get().getAttributes().densityPixels;
        mCycle = (mDensity * mCycle);
        mDrawFinish = true;

        mRevealPaint = new Paint();
        mRevealPaint.setAntiAlias(true);
        mRevealPaint.setColor(new Color(adjustAlpha(mRippleColor, mAlphaFactor)));

        mStatusBarHeight = Integer.valueOf(AttrUtils.invokeStringValue("ro.config.hw_notch_size", "288,129,0,0").split(",")[1]);
        AttrUtils.debug("mStatusBarHeight = " + mStatusBarHeight);
    }

    private void startAnimation() {
        mRadiusAnimator = new AnimatorValue();
        mRadiusAnimator.setDuration(mDuration);
        mRadiusAnimator.setLoopedCount(AnimatorValue.INFINITE);
        mRadiusAnimator.setCurveType(AnimatorValue.CurveType.ACCELERATE_DECELERATE);
        mRadiusAnimator.setValueUpdateListener((animatorValue, var2) -> {
            invalidate();
        });
        mRadiusAnimator.start();
    }

    private void updateDrawData() {
        setDrawRadius(DRAW_RADIUS_ZERO);
        /**
         * 计算最大半径
         */
        int radiusLeftTop = (int) Math.sqrt(mDownX * mDownX + mDownY * mDownY);
        int radiusRightTop = (int) Math.sqrt((mRect.right - (mDownX + getLeft())) * (mRect.right - (mDownX + getLeft())) +
                mDownY * mDownY);
        int radiusLeftBottom = (int) Math.sqrt(mDownX * mDownX +
                (mRect.getHeight() - mDownY) * (mRect.getHeight() - mDownY));
        int radiusRightBottom = (int) Math.sqrt((mRect.right - (mDownX + getLeft())) * (mRect.right - (mDownX + getLeft())) +
                (mRect.getHeight() - mDownY) * (mRect.getHeight() - mDownY));
        mMaxRadius = AttrUtils.getMax(radiusLeftTop, radiusRightTop, radiusLeftBottom, radiusRightBottom);

        mStepRadius = mMaxRadius / mCycle;
    }

    /**
     * adjustAlpha
     *
     * @param color  color
     * @param factor factor
     * @return int
     */
    public int adjustAlpha(int color, float factor) {
        int alpha = Math.round(Color.alpha(color) * factor);
        int red = AttrUtils.red(color);
        int green = AttrUtils.green(color);
        int blue = AttrUtils.blue(color);
        return Color.argb(alpha, red, green, blue);
    }

    private int dp(int dp) {
        return (int) (dp * mDensity + 0.5d);
    }

    /**
     * setDrawRadius
     *
     * @param radius radius
     */
    public void setDrawRadius(final float radius) {
        mDrawRadius = radius;
    }

    /**
     * onTouchEvent
     *
     * @param component  component
     * @param touchEvent touchEvent
     * @return boolean
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        int action = touchEvent.getAction();
        isPointMove = false;
        switch (action) {
            case TouchEvent.PRIMARY_POINT_DOWN: {
                if (!mHover) {
                    break;
                }

                mPressUp = false;
                mDrawFinish = false;
                mDownX = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                mDownY = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
                updateDrawData();
                break;
            }
            case TouchEvent.POINT_MOVE:
                if (!mHover) {
                    break;
                }
                if (mDrawFinish) {
                    mDrawFinish = false;
                }
                isPointMove = true;
                int downX = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                int downY = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
                if (!mRect.isInclude(downX + getLeft(), downY - mStatusBarHeight + getTop())) {
                    setDrawRadius(DRAW_RADIUS_ZERO);
                } else {
                    mDownX = downX;
                    mDownY = downY - mStatusBarHeight;
                    updateDrawData();
                    setDrawRadius(dp(DRAW_RADIUS_MOVE));
                }
                break;
            case TouchEvent.CANCEL:
            case TouchEvent.PRIMARY_POINT_UP:
                mStepRadius = (int) (10 * mStepRadius);
                mPressUp = true;
                if (mDrawFinish) {
                    mDrawFinish = false;
                    setDrawRadius(dp(DRAW_RADIUS_MOVE));
                }
                break;
        }
        return true;
    }

    /**
     * onDraw
     *
     * @param component component
     * @param canvas    canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mDrawFinish) {
            return;
        }
        if (mStepRadius == 0) {
            return;
        }

        if (!isPointMove) {
            mDrawRadius += (double) mStepRadius;
        }

        if (mDrawRadius > dp(DRAW_RADIUS_MOVE) && !mPressUp) {
            setDrawRadius(DRAW_RADIUS_ZERO);
            mDrawFinish = true;
        }

        if (mDrawRadius > mMaxRadius) {
            setDrawRadius(DRAW_RADIUS_ZERO);
            canvas.drawCircle(mDownX, mDownY, mMaxRadius, mRevealPaint);
            clipCanvas(canvas);
            mDrawFinish = true;
            return;
        }
        canvas.drawCircle(mDownX, mDownY, mDrawRadius, mRevealPaint);
        clipCanvas(canvas);
    }

    /**
     * setClipPath
     *
     * @param clipPath clipPath
     */
    public void setClipPath(Path clipPath) {
        this.mClipPath = clipPath;
    }

    /**
     * clipCanvas
     *
     * @param canvas Canvas
     */
    public void clipCanvas(Canvas canvas) {
        if (mClipPath != null) {
            Paint paint = new Paint();
            paint.setColor(new Color(Color.getIntColor("#FF9999"))); //背景色
            paint.setStyle(Paint.Style.FILL_STYLE);
            final Path path = new Path();
            path.addRect(0, 0, getWidth(), getHeight(), Path.Direction.CLOCK_WISE);
            canvas.clipPath(mClipPath, Canvas.ClipOp.DIFFERENCE);
            canvas.drawPath(path, paint);
        }
    }
}
